从一个群友问题看流复制实现原理
前言
这个问题是前几天②群一位群友问的
问个问题,未提交得事务,WAL也会同步到从节点吗?
当时我的回复是:"嗯,然后根据commit和rollback,我下来确认一下"。借着这个问题,我又重新思考了一下流复制的原理🤔,又有了一些新的思考,甚好甚好。
分析
首先这个问题的答案无疑,是肯定会同步到从节点,这一点和逻辑复制有所不同,之前也写过一篇逻辑复制处理大事务的演进,在14版本逻辑复制才正式引入了"流式"传输,即未提交的事务也会流式传输到订阅端,以前都是直到事务提交时才会一股脑发送至订阅端,所以碰到大事务就歇菜。
但是,流复制和逻辑复制有所不同,这一点其实很好验证 (以下截图左边是主库,右边是备库):
主库在事务内做建表、插入等操作,但是事务还未提交。👆🏻可以看到,备库中pg_waldump可以解析到mytest表的oid,以及插入操作。最后,随着主库的提交或者回滚以决定元组的可见性,主库提交备库也提交,主库回滚备库也相应回滚。
现在我们新插入一条数据,各位可以看到,备库 startup 也"及时"回放到页面上了。但是主库上由于事务还未提交,因此备库不可见,但是底层数据块上已经有这条数据了,说明startup进程已经及时回放了这条WAL。
因此,备库也会产生死元组 (update、delete、事务回滚等),也需要相应进行 VACUUM,移除死元组,从而导致流复制场景中一个很常见的冲突——snapshot conflict,即快照冲突,备库查询需要的行版本被 VACUUM 移除了,从而报错:User query might have needed to see row versions that must be removed.
所以,一环扣一环,从很多平时细节,我们就可以倒推出这个原理。我们不如通俗地理解成:备库按部就班,原模原样复刻主库的操作。主库回滚我就回滚,主库删除我就删除,也正是这种数据块级别的物理复制机制,使得流复制有着其独特的优势——一致性。
深入剖析
作为一名咸鱼DBA,需要有打破砂锅问到底的精神,我们不妨思考一下,PostgreSQL的流复制原理,底层是如何实现的?此处站在巨人的肩膀上——SUZUKI,内幕指南已经更新了流复制章节,我也很早就看了,https://www.interdb.jp/pg/pgsql11.html,其中也有介绍流复制的传输细节:
(1) The backend process writes and flushes WAL data to a WAL segment file by executing the functions XLogInsert() and XLogFlush(). (2) The walsender process sends the WAL data written into the WAL segment to the walreceiver process. (3) After sending the WAL data, the backend process continues to wait for an ACK response from the standby server. More precisely, the backend process gets a latch by executing the internal function SyncRepWaitForLSN(), and waits for it to be released. (4) The walreceiver on the standby server writes the received WAL data into the standby's WAL segment using the write() system call, and returns an ACK response to the walsender. (5) The walreceiver flushes the WAL data to the WAL segment using the system call such as fsync(), returns another ACK response to the walsender, and informs the startup process about WAL data updated. (6) The startup process replays the WAL data, which has been written to the WAL segment. (7) The walsender releases the latch of the backend process on receiving the ACK response from the walreceiver, and then, the backend process's commit or abort action will be completed. The timing for latch-release depends on the parameter synchronous_commit. It is 'on' (default), the latch is released when the ACK of step (5) received, whereas it is 'remote_write', the latch is released when the ACK of step (4) is received.
翻译一下
后端进程通过执行XLogInsert和XLogFlush函数,将WAL数据写入并刷新到WAL段文件中。 walsender进程将写入WAL段的WAL数据发送给walreceiver进程。 发送WAL数据后,后端进程继续等待备用服务器的ACK响应。更准确地说,后端进程通过执行内部函数SyncRepWaitForLSN获得一个latch,并等待它被释放。 备用服务器上的walreceiver将接收到的WAL数据写入备用的WAL段中,使用write系统调用,并向walsender返回一个ACK响应。 walreceiver使用如fsync等系统调用将WAL数据刷新到WAL段,向walsender返回另一个ACK响应,并通知启动进程关于WAL数据的更新。 startup进程回放已写入WAL段的WAL数据。 当walsender收到来自walreceiver的ACK响应时,释放后端进程的 latch,然后后端进程的提交或中止操作将完成。latch释放的时机取决于参数synchronous_commit。如果设为on (默认值),当收到步骤(5)的ACK时释放latch;如果设为remote_write,则在收到步骤(4)的ACK时释放latch。
每一个ACK都包括
The LSN location where the latest WAL data has been written. The LSN location where the latest WAL data has been flushed. The LSN location where the latest WAL data has been replayed in the startup process. The timestamp when this response has be sent.
根据上述传输细节,我们便可以对这个案例进行理解剖析了:
主库上的进程写操作 产生WAL record walsender感知到新的WAL,发送给备库, 备库接受,写盘,再回放
因此准确来说,是否发送WAL与主库的事务是否提交与否没有关系,但主库的事务能否提交却取决于备库的WAL写到了哪里(默认是on的话,就需要落盘),即同步级别,备库会实时回放。
至于latch,此次PostgreSQL生态大会上,文一也做了介绍,流复制就是借助latch实现主从进程间的协作。
小结
不难理解,流复制以低延时(相对)著称,是否发送WAL与主库的事务是否提交与否没有关系,但主库的事务能否提交取决于备库的WAL写到了哪里(synchronous_commit),假如还要等待事务提交的时候,才将所有的WAL发送,那么无疑延迟会很大,这也体现不了物理复制的优势。
不过值得注意的是,流复制场景下,只读事务、子事务的提交以及事务回滚,无需等待备库的ACK。
Read-only transactions and transaction rollbacks need not wait for replies from standby servers. Subtransaction commits do not wait for responses from standby servers, only top-level commits. Long running actions such as data loading or index building do not wait until the very final commit message. All two-phase commit actions require commit waits, including both prepare and commit.
最后,再次推荐各位阅读一下SUZUKI大师的最新流复制章节,相信会有更多理解。
参考
文一 PostgreSQL Latch 简介:一种事件实现机制
Streaming Replication